﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JihankiLib
{
    /// <summary>
    /// <para>購入操作に対する応答</para>
    /// </summary>
    public interface IJihankiResult
    {
        /// <summary>
        /// 応答の取得
        /// </summary>
        /// <remarks>
        /// <para>各実装クラスコンストラクタで、書き込み先オブジェクトを渡す。</para>
        /// <para>OpenResultすると、そこに結果情報が出力される。</para>
        /// </remarks>
        void OpenResult();
    }

    /// <summary>
    /// 商品棚
    /// </summary>
    public class ItemBoxUnit
    {
        public int No { get; private set; }

        public ItemBoxUnit(int unitNo)
        {
            this.No = unitNo;
        }
    }

    /// <summary>
    /// 商品棚通知イベント情報
    /// </summary>
    public class JihanItemBoxNoticeEventArgs : EventArgs
    {
        public ItemBoxUnit Unit { get; set; }
    }

    /// <summary>
    /// アイテム売上イベント情報
    /// </summary>
    public class JihanItemPurchasedEventArgs : EventArgs
    {
        public ItemBoxUnit Unit { get; set; }
        public MoneySet Value { get; set; }
    }

    /// <summary>
    /// 自販機アイテムストック
    /// </summary>
    public class ItemStock
    {
        private Queue<IJihankiResult> itemQueue = new Queue<IJihankiResult>();
        public int Value { get; private set; }
        public int UnitNo { get; private set; }
        public int Count { get { return itemQueue.Count; } }

        public ItemStock(int no, int value)
        {
            this.UnitNo = no;
            this.Value = value;
        }

        public bool ExistStock()
        {
            return itemQueue.Count != 0;
        }

        public void Add(IJihankiResult item)
        {
            itemQueue.Enqueue(item);
        }

        public IJihankiResult Out()
        {
            return itemQueue.Dequeue();
        }
    }

    public class InMoneyStatusChangedEventArgs : EventArgs
    {
        public int CurrentMoney { get; set; }
    }

    /// <summary>
    /// 商品棚の制御機器
    /// </summary>
    public class JihankiItemBoxMachine : IObserver<JihankiMessage>
    {
        /// <summary>
        /// 棚が空になった
        /// </summary>
        internal event EventHandler<JihanItemBoxNoticeEventArgs> Empty;
        /// <summary>
        /// (空になった)棚が補充された
        /// </summary>
        internal event EventHandler<JihanItemBoxNoticeEventArgs> ItemReplenished;
        /// <summary>
        /// 商品が購入された
        /// </summary>
        internal event EventHandler<JihanItemPurchasedEventArgs> ItemPurchased;
        /// <summary>
        /// お金の状態が変わった
        /// </summary>
        public event EventHandler<InMoneyStatusChangedEventArgs> InMoneyStatusChanged;


        protected void OnItemPurchased(int unitNo, int value)
        {
            if (ItemPurchased != null)
                ItemPurchased(this, new JihanItemPurchasedEventArgs() { Unit = new ItemBoxUnit(unitNo), Value = MoneyParser.Current.Parse(value) });
        }

        protected void OnEmpty(int unitNo)
        {
            if (Empty != null)
                Empty(this, new JihanItemBoxNoticeEventArgs() { Unit = new ItemBoxUnit(unitNo) });
        }

        public JihankiItemBoxMachine()
        {
        }

        private IDisposable jihanki;

        public void Initialize(IJihanki jihanki)
        {
            this.jihanki = jihanki.Subscribe(this);
        }

        public virtual void PurchaseItem(int unitNo, int value, ItemStock stock)
        {
            if (!stock.ExistStock())
                return;
            if (stock.Value > value)
                return;
            
            var item = stock.Out();
            OnItemPurchased(unitNo, value);
            item.OpenResult();

            if(!stock.ExistStock())
                OnEmpty(unitNo);
        }

        public virtual void OnCompleted()
        {
            jihanki.Dispose();
        }

        public virtual void OnError(Exception error)
        {
            
        }

        public virtual void OnNext(JihankiMessage value)
        {
            //Todo: 各MessageProcesserクラスを作成し、そこにメッセージごとの処理を移動する。

            if (value.GetType() == typeof(AddedMoneyMessage))
            {
                var msg = value as AddedMoneyMessage;
                if (InMoneyStatusChanged != null)
                    InMoneyStatusChanged(this, new InMoneyStatusChangedEventArgs() { CurrentMoney = msg.Total.Value });
            }

            if (value.GetType() == typeof(RejectedAllMoneyMessage))
            {
                if (InMoneyStatusChanged != null)
                    InMoneyStatusChanged(this, new InMoneyStatusChangedEventArgs() { CurrentMoney = 0 });
            }
        }
    }

}
